package com.leo.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @author du
 */
public class GroupChatServer {

    private Selector selector;
    private ServerSocketChannel listenerChannel;
    private static final int PORT = 6667;


    public GroupChatServer() {
        try {
            selector = Selector.open();
            listenerChannel = ServerSocketChannel.open();
            listenerChannel.socket().bind(new InetSocketAddress("127.0.0.1", PORT));
            listenerChannel.configureBlocking(false);
            listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    public void listen() {
        try {
            while (true) {
                if(selector.select(2000) == 0) {
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    // 有新连接到来, 提示上线
                    if (key.isAcceptable()) {
                        SocketChannel socketChannel = listenerChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println(socketChannel.getRemoteAddress() + " 已上线.");

                        //上线信息需要转发到所有channel(排除自己) TODO
                    }

                    // 客户端发送消息，通道发生可读事件
                    if (key.isReadable()) {
                        // 处理读
                        doRead(key);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {

        }
    }

    protected void doRead(SelectionKey key) {
        SocketChannel channel = null;
        String msg = "";
        try {
            channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(5);
            int read;
            while ((read = channel.read(buffer)) > 0) {
                // TODO 中文可能会被截断导致乱码
                msg += new String(buffer.array(), 0, read);
                buffer.clear();
            }
            if (read == -1) {
                offline(key, channel); // read返回-1表明客户端的数据发送完毕，并且主动的close socket
            }
            System.out.println("来自客户端 " + channel.getRemoteAddress() + " 的消息: " + msg);
            // 消息转发到所有channel(排除自己)
            if (null != msg && !"".equals(msg)) {
                doBroadcast(msg, channel);
            }

        } catch (IOException e) {
            offline(key, channel); //客户端主动关闭socket时，若服务端继续read，会抛出`远程主机强迫关闭一个现有的连接`
            System.out.println(e.getMessage());
        }
    }

    protected void offline(SelectionKey key, SocketChannel channel) {
        try {
            System.out.println(channel.getRemoteAddress() + "离线了");
            // 取消注册
            key.cancel();
            // 关闭通道
            channel.close();
        } catch (IOException e1) {
            System.out.println(e1.getMessage());
        }
    }

    protected void doBroadcast(String msg, SocketChannel self) throws IOException {
        System.out.println("服务器转发消息中...");
        for (SelectionKey key : selector.keys()) {
            Channel channel = key.channel();
            // 排除自己
            if (channel instanceof SocketChannel && channel != self) {
                ((SocketChannel) channel).write(ByteBuffer.wrap(msg.getBytes()));
            }
        }
    }

    public static void main(String[] args) {
        GroupChatServer server = new GroupChatServer();
        server.listen();
    }
}
